;   This program is free software: you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation, either version 3 of the License, or
;   (at your option) any later version.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program.  If not, see <http://www.gnu.org/licenses/>.
;
;程序名称:HEXVIEW.ASM
;功能:文件的十六进制观看器 HEX VIEWER
;环境:16BIT DOS实模式(windows/dos或dosbox)
;编译器:MASM 5.1 (这个是com格式，masm5.x须用EXE2BIN HEXVIEW.EXE HEXVIEW.COM，Masm6.x须加入：ml /ＡＴ参数
;用法:看说明
;返回值:没有
;破坏寄存器:不适用
;
;使用方法: HEXVIEW <Filename>
;程式会读入档案,并以十六进制显示,按上下pgup,pgdown,home,end观看,ESC离开
;为减少复杂度,最大读入档案大小512k,大于则只读入前512k部份
;
;注意1:这只是一个范例程式，具备最起码的功能，没有太花巧的技术.
;若你需要其他功能或加入或删减任何代码，请随便，但不能要求作者为你订制特定的功能或
;根据功课要求而改变这个子程序改变那个子程序，若是代码错误或优化代码的意见，
;则欢迎提出:)
;
;注意1:伸手党拿去取用没问题，但[不能]在代码任何位置或标题或程式上加上个人资料
;例如作者谁谁谁，或者Written by XXX等等，这代码是本人的烂创作，不是谁或谁写的东西
;本人都不加名字了！
;
;程序只加了一些简单解释，若要则深入理解程式运作，请自行用debug，不要问我。
;
;代码及程式;
;http://pan.baidu.com/s/1i3zUvvn
;
.286
esc_key         equ     01h ;定義按鍵
up_key          equ     48h
down_key        equ     50h
left_key        equ     4bh
right_key       equ     4dh
home_key        equ     47h
end_key         equ     4fh
pgup_key        equ     49h
pgdn_key        equ     51h
text_color      equ     1Bh ;定義顏色
offset_color    equ     1Eh
char_color      equ     1Ah
title1_color    equ     30h
title2_color     equ     34h ;30h
back_color      equ     17h
dos_max_seg     equ     8  ;最大段數 8 個 8 x 64 = 512k
space_colAtt    equ     1720h ;空白加顏色值 藍底

code_seg        segment
                assume  cs:code_seg,ds:nothing,es:nothing
                org     100h  ;com 檔格式
start:          jmp     begin

help_str        db      'Usage: HexV filename',10,13,'$'
error_str    db	10,13,'Something Error !$'
handle		dw	0ffffh  ;句柄
file_seg	dw	0              ;申請內存段地址
file_size	dw 	0,0       ;檔大小
max_line	dw	0           ;最大列
cursor_size     dw      0  ;光標大小
line            dw      0  ;列變數,上下可改變
Vmax_line       dw      0  ;最大容許列
Lo_value        dw      0  
Hi_value        dw      0
hex_table       db      '0123456789ABCDEF' ;十六進制表
max_seg         dw      dos_max_seg   ;8  ;實際獲得的數,多少64k
line_count      dw      0       ;23   ;屏幕容許的最大列數 = 23 , 減去2列標題

Title1 label byte
db '    <<<<    HEX VIEWER v0.1 (2015)   >>>>         ',24,25,',PgUp,PgDn,Home,End Esc=Quit'
Title2 label byte
db '- Offset -  ----------------'
name_off db '-------------------------------    -- ASCII value --'

key_table       db      up_key,down_key,pgup_key,pgdn_key,home_key,end_key,esc_key ;按鍵表
no_of_key       equ     $ - offset key_table  ;表長度

process         dw      offset up  ;以下是對應上下等鍵的子程序進入點
		dw	offset down
                dw      offset pgup
                dw      offset pgdn
                dw      offset home
                dw      offset end_proc
		dw	offset Esc_proc

;-------- code start ------------------
begin:          cld             ;清除方向標誌
                mov     ax,cs  ;因為是com, cs和ds,es對齊
                mov     ds,ax
                mov     es,ax
                mov     si,82h ;參數地址第一字元偏移地址
                mov     cl,[si-2]  ;ds=psp  ;取得參數字元個數
		mov 	ch,0  ;清0
                jcxz    help   ;沒有輸入,離開印出說明
		mov 	dx,si ;檔名地址
		add	si,cx ;加長度
		mov 	byte ptr [si-1],0		;清除最末的回車0d
                mov     ax,3d00h     ;開檔
		int	21h
Error1:		mov 	dx,offset error_str ;印出錯誤
		jc	error2  ;失敗則離開
		mov 	handle,ax ;存句柄
                mov     di,offset name_off ;指向二列標題的位置
                mov     si,81h ;由參數區81h開始
st10:           lodsb  ;請字元
                or      al,al  ;是否0
                jz      st20  ;是
                mov     ah,al ;存ah
                and     ah,11011111b  ;轉大寫
                cmp     ah,'A'  ;以下比較是否A-Z
                jb      st15  ;不是
                cmp     ah,'Z'
                ja      st15  ;不是
                mov     al,ah  ;把大寫的存al
st15:           stosb           ;存到標題
                jmp     short st10

st20:           mov     al,20h ;最後加一空白
                stosb
                call    cls     ;清屏
                call    clsc    ;繪背景
                mov     ah,3    ;讀光標 
                xor     bh,bh
                int     10h
                mov     cursor_size,cx ;存原來光標大小
                call    closecur  ;關閉光標
                call    hex_main       ;主程序
                jmp     short quit ;離開
Help:           mov     dx,offset help_str ;印出說明
Error2:         mov     ah,9
		int	21h
quit:           mov     ah,4ch ;返回dos
                int     21h
;---------------------------------------------------
hex_main:  ;主程序
                mov     bx,offset proc_end  ;指向整個程序末
                mov     sp,bx  ;修改棧頂
                mov     cl,4   
                shr     bx,cl  ; 除16 ,即一段
                inc     bx     ;補1段
                mov     ah,4ah  ;由psp : 0 到 程序末的大小/16,得段大小,用這段來調整可用內存
                int     21H
		jc	hex_err
                mov     bx,dos_max_seg * 1000h                ;8個64k
mem_apply:      mov     ah,48h          ;Allocate Memory  ;申請內存
                int     21h 
                jnc     mem_ok          ;成功則 mem_ok
                sub     bx,1000h        ;失敗,則減64k
                dec     max_seg         ;減64k個段
                jnz     mem_apply       ;不等于0,回去再申請
                jmp     short hex_err   ;等于0,則無法申請那怕是一個64k,黯然離開
mem_ok:
                mov     file_seg,ax     ;申請得來的段起點地址
                call    clear_seg       ;請除所得的段內容,全部填上FFFF
                call    read_data       ;請檔內容
		jc	hex_err                ;讀取錯誤,離開
                call    print_header    ;印標題

display:        call    display_data    ;印十六進制內容

rekey:          push    cs          ;cs,es對齊
                pop     es
                mov     ah,0            ;讀鍵
		int 	16h
                mov     al,ah           ;存al,即掃瞄碼
                mov     cx,no_of_key     ;比較鍵表長度
		mov	di,offset key_table   ;鍵表
		repnz	scasb   ;找
                jnz     rekey  ;找不到,回去再讀鍵
		sub	cx,no_of_key  ;減去長度
		neg	cx  ;補
		dec	cx ;調整
		mov	bx,cx  ;得出該鍵在表中位置
		shl	bx,1   ; x 2 ,因為子程序是字 word 
		mov	si,offset process  ;程序表起點
                call    word ptr cs:[si+bx]  ;指向各鍵的子程序
                jc      rekey          ;cf=1 則再讀新鍵,即不用刷新資料
                jmp     short display  ;cf=0 刷新資料

hex_err:        stc   ;錯誤, cf=1
		ret

hex_ok:		clc ;cf=0
		ret
;---------------------------------------------------
clear_seg:      push    es   ;以下是清除段,填上ffff
                mov     bx,file_seg
                mov     bp,max_seg
                mov     ax,-1h   ;ffff

clearSeg10:     mov     es,bx
                mov     di,0
                mov     cx,8000h /2
                rep     stosw
                mov     cx,8000h /2
                rep     stosw
                dec     bp
                jz      clearSeg20
                add     bx,1000h
                jmp     short clearSeg10

clearSeg20:     pop     es
                ret
;---------------------------------------------------
;讀檔子程序
read_data:	push 	ds
		mov 	bx,handle  ;句柄
		mov 	si,file_seg  ;指向段地址
                mov     ax,max_seg  ;最大64 數
                mov     line_count,ax ;暫存在count變數

read10:		mov	ds,si  ;存段
                mov     di,2  ;讀2次 8000h ,合共 64k
                mov     dx,0  ;ds:0 存入0 偏移

read15:         mov     cx,8000h  ;64k / 2 
		mov 	ah,3fh  ;讀檔
		int 	21h
		jc	read_err  ;錯
		or 	ax,ax  ;是否0,即讀到檔末
		jz	read20 ;跳
                cmp     ax,8000h ;是否讀到8000h
		jnz	read20  ;不是
                mov     dx,8000h ;指向ds: 8000h,即下半64K
                dec     di  ; 2 次
                jnz     read15 ;讀了2次沒? 還沒,回去再讀
                inc     file_size + 2  ;讀了64k, 32bit的檔大小變數高位加1 ,即 0001:0000
		add	si,1000h	;next segment ;下一64k段
                dec     line_count ;減已申請的64k個數 - 1
                jnz     short read10 ;還未到0,再讀

read20:         cmp     di,2  ;是否2
                jz      read27  ;是,則只讀了第一次
                add     ax,8000h ;不是,即已請了第一次8000h, ax是最後的大小,加8000h
read27:
                mov     file_size,ax  ;存32bit的檔大小變數低位 ,即 0000:xxxx
                mov     dx,file_size + 2 ;取32bit的檔大小變數高位
                mov     ax,file_size   ;取32bit的檔大小變數低位
		mov 	cx,16   
		div	cx                ;dx:dx 除16
                mov     max_line,ax  ;一列顯示16bytes,得最大列數,因為申請最大512k內,除16的結果,一定少65535
                cmp     ax,25 - 2  ; 比較 (屏幕列數-標題)
                ja      read30  ;  ;大于跳
                mov     Vmax_line,0 ;少于,檔太小,一屏已顯示完,最大列數限制是0,則上下鍵沒反應
                jmp     short read40

read30:         sub     ax,25 - 2 ;列數 -   (屏幕列數-標題)
                inc     ax              ;調整
                mov     Vmax_line,ax  ;列變數的最大限制

read40:         mov     bx,handle  ;取句柄
		mov 	ah,3eh  ;關檔
		int	21h
		clc   ;cf = 0
		jmp	short readx

read_err:	stc  ;cf =1

readx:		pop	ds
		ret
;---------------------------------------------------
display_data:   push    es   ;以下是顯示十六進制子程序
                push    ds
                mov     line_count,23  ;顯示列數
                mov     ax,0b800h       ;指向顯示緩衝
                mov     es,ax        ;存ES
                mov     di,(80 * 2)  * 2 ;line 3  ;指向第三列
                mov     ax,space_colAtt ;空白
                stosw
                mov     ax,line     ;列變數

display10:      push    ax          ;保存印出列
                push    di
                push    ax          
                call    print_offset ;印出列變數對應的檔偏移
                mov     ax,space_colAtt 
                stosw       ;印出三個空白
                stosw
                stosw                   
                pop     ax  ;回存列
                mov     bx,file_seg ;指向資料段起點
                mov     cx,16 
                mul     cx   ;列 x 16 ,真正檔偏移
                ;       dx:ax  ;積在ds:dx
                mov     si,ax  ;段內偏移,存si
display20:
                or      dx,dx   ;是否0
                jz      display30 ;是
                add     bx,1000h ;不是,加64k
                dec     dx  ;減 1
                jmp     short display20 ;回去再試,直到指向正確的64k段

display30:      mov     ds,bx           ;get segment ;段地址
                mov     bp,16           ;1 line 印出16字節

                push    si          ;保存
display40:      lodsb               ;請入字節
                mov     dh,al       ;存dh
                mov     ch,2        ;2次 
                mov     ah,text_color ;色
                call    store_hex       ;output 00 2 chars ;印出dh的ASCII,若dh=FB,印出文字的FB
                mov     ax,space_colAtt  
                stosw       ;一個空白
                dec     bp  ;減1
                jnz     display40 ;未到16再印
                mov     ax,space_colAtt 
                mov     cx,3
                rep     stosw ;三個空白
                pop     si  ;回存si  ;

                mov     cx,16           ;1 line 
                mov     ah,char_color
display50:      lodsb
                stosw           ;印出原來一列的資料
                loop    display50       ;16次
                mov     ax,space_colAtt 
                stosw       ;一空白
                ;       1 line finish  ;完成一列
                pop     di ;回存di
                add     di,80 * 2       ;1 line;下一列
                pop     ax      ;回存列
                inc     ax      ;下一列
                dec     line_count ;是否23列
                jnz     display10 ;不是,回去再列印
                pop     ds
                pop     es
                ret
;---------------------------------------------------
print_offset:   mov     cx,16  ;印出檔案偏移子程序
                mul     cx
                ;       dx:ax  ;ax列x16 ,結果在dx:ax
                mov     Lo_value,ax  ;存于變數
                mov     Hi_value,dx
                mov     ah,offset_color ;色
                call    disp_hex  ;印十六進制值
                ret
;---------------------------------------------------
print_header:           ;印出標題子程序
                push    cs
                pop     es 
                xor     dx,dx   ;0,0  ;清0
                mov     bp,offset title1  ;標題1
                mov     cx,80
                mov     bh,0
                mov     bl,title1_color ;色
                mov     ax,1300h
                int     10h  ;印出
                ;
                inc     dh
                mov     bp,offset title2 ;標題2
                mov     cx,80
                mov     bh,0
                mov     bl,title2_color ;色
                mov     ax,1300h
                int     10h
                ret

;---------------------------------------------------
; 上鍵子程序
up:             cmp     line,0  ;是否0
                jz      upx  ;是,走
                dec     line  ; 減1
                clc
                ret

upx:            stc
                ret
;---------------------------------------------------
 ;下鍵子程序
down:           mov     ax,line   ;取line
                cmp     ax,vMax_line  ;是否到限制
                jae     downx  ;是,走
                inc     line  ;加列變數
                clc
                ret

downx:          stc
                ret
;---------------------------------------------------
 ;上頁鍵子程序
pgup:           mov     cx,22  ;22次
                mov     bx,cx  ;存

pgup10:         call    up    ;呼叫up
                jc      pgup20  ;是否失敗
                loop    pgup10  ;做22次
pgup20:         cmp     cx,bx  ;是否一樣
                jz      pgupx  ;一樣則這次pgup連一次[上]都做不到,cf=1離開,不做屏幕刷新
                clc   ;cx,bx不一樣,即做了一次或以上的up,則cf=0,做屏幕刷新
                ret
pgupx:
                stc
		ret
;---------------------------------------------------
 ;下頁鍵子程序
pgdn:           mov     cx,22  ;22次
                mov     bx,cx  ;存

pgdn10:         call    down  ;呼叫down
                jc      pgdn20 ;是否失敗
                loop    pgdn10 ;做22次
pgdn20:         cmp     cx,bx ;是否一樣
                jz      pgdnx ;一樣則這次pgdn連一次[下]都做不到,cf=1離開,不做屏幕刷新
                clc   ;cx,bx不一樣,即做了一次或以上的down,則cf=0,做屏幕刷新
                ret
pgdnx:
                stc
		ret
;---------------------------------------------------
 ;home鍵子程序
home:           cmp     line,0   ;是否0
                jz      homex  ;是0,離開
                mov     line,0 ;不是0,設line = 0
                clc
                ret

homex:          stc
                ret
;---------------------------------------------------
 ;end鍵子程序
end_proc:       mov     ax,line  ;取line
                cmp     ax,vMax_line  ;是否到了列限制
                jae     endx  ;大于,走
                mov     ax,vMax_line  ;取列限制
                mov     line,ax ;存line
                clc
                ret

endx:           stc
		ret
;---------------------------------------------------
Esc_proc:       mov     ax,0003h 
                int     10h  ;清屏, 等于cls
                call    quit  ;去dos
		ret
;---------------------------------------------------
;清屏子程序
cls:		mov	ah,0fh                  ;get display mode to al
		int	10h
		mov	ah,0                    ;Set display mode
		int	10h
		ret
;--------------------------------------------------------
;繪背景屏子程序,
clsc:           mov     ax,0600h                 ;cls
                mov     bh,back_color            ;attribute ;藍
                mov     cx,0                     ;top left
                mov     dl,80
                mov     dh,60
                int     10h
                ret

;------------------------------------------------------
;設定光標子程序
setcur:         push    ax
                push    bx
                mov     ah,2
                xor     bh,bh
                int     10h
                pop     bx
                pop     ax
                ret
;-----------------------------------------------------
;關閉光標子程序
closecur:       pusha
                mov     cx,3030h
                mov     ah,1
                int     10h
                popa
                ret
;-----------------------------------------------------
disp_hex:       ;顯示16進制
                mov     dx,Hi_value ;最高位
		mov 	si,2 ;2次 高:低
		; dx = prev_hi			
                ;mov     ah,text_color ;色

change_15:      mov     ch,4  ;4次
                call    store_hex ; 印 0000  
                mov     dx,Lo_value  ;取低位
		dec	si 
		jnz	change_15
		ret
;---------------------------------------------------

; dx,dl      ch may be 2 or 4 for 00 or 0000 ;印出dx或dh十六進制字符,示ch=2 or 4

store_hex:      mov     bx,offset hex_table

store_h10:      mov     cl,4
                rol     dx,cl
                mov     al,dl
                and     al,00001111b
                db      2eh     ;CS:
                xlatb
                stosw
                dec     ch
                jnz     store_h10
                ret

;----------------------------------------------------------------------------
proc_end        equ $ + 200h    ;sp
code_seg        ends
                end     start
